package ast;

import java.util.ArrayList;
import java.util.HashMap;

public class Expression extends AstItem {
	// 表达式
	public enum OperatorType {
		OP_ADD, OP_MINU, OP_MUL, OP_DIV, OP_MOD, OP_NOT, OP_AND, OP_OR,
		OP_LSS, OP_LEQ, OP_GRE, OP_GEQ, OP_EQ, OP_NEQ, OP_ARRAY_CONCAT, OP_ATOM
	}
	// 新加字段，预处理做类型推断后填上
	public String dataType;
	public OperatorType operator;
	// Expression类 / DataSignature类
	// 如果operator == OP_ATOM，第一个退化成DataSignature类
	public ArrayList<AstItem> operands = new ArrayList<AstItem>();
//	private AstItem first_operand;
//	// Expression类
//	private AstItem second_operand;

	public ArrayList<AstItem> allUnaryExp = new ArrayList<>();
	
	public String toString() {
		if (operator == OperatorType.OP_ADD) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " + " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_MINU) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " - " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_MUL) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " * " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_DIV) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " / " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_MOD) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " % " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_AND) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " && " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_OR) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " || " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_LSS) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " < " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_LEQ) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " <= " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_GRE) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " > " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_GEQ) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " >= " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_EQ) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " == " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_NEQ) {
			assert operands.size() == 2;
			return operands.get(0).toString() + " != " + operands.get(1).toString();
		} else if (operator == OperatorType.OP_ATOM) {
			assert operands.size() == 1;
//			System.out.println(operands.get(0).getClass());
			return "(" + operands.get(0).toString() + ")";
		} else if (operator == OperatorType.OP_NOT) {
			assert operands.size() == 1;
			return "!" + operands.get(0).toString();
		} else if (operator == OperatorType.OP_ARRAY_CONCAT) {
			String result = "{";
			for (int i = 0;i < operands.size();i++) {
				result += operands.get(i).toString();
				if (i != operands.size() - 1) result += ", ";
			}
			result += "}";
			return result;
		}
		return "";
	}
	
	private HashMap<String, Integer> type2priority = new HashMap<String, Integer>(){{
		put("bool", 0);
		put("char", 1);
		put("int", 2);
		put("long", 3);
		put("longlong", 4);
		put("double", 5);
	}};
	
	private HashMap<Integer, String> priority2type = new HashMap<Integer, String>(){{
		put(0, "bool");
		put(1, "char");
		put(2, "int");
		put(3, "long");
		put(4, "longlong");
		put(5, "double");
	}};

	
	public String getDataType(AstItem x) {
		assert ((x instanceof PrimaryDataSignature) || (x instanceof ComplexDataSignature) || (x instanceof Expression));
		if (x instanceof PrimaryDataSignature) {
			return ((PrimaryDataSignature) x).dataType;
		} else if (x instanceof ComplexDataSignature) {
			return ((ComplexDataSignature) x).dataType;
		} else {
			// 表达式
			return ((Expression) x).dataType;
		}
	}

	public void preprocess(AstItem father) {
		for (AstItem x : operands) {
			x.preprocess(this);
		}
		// 表达式的类型推断
		if (operator == OperatorType.OP_ATOM) {
			assert operands.size() == 1;
			this.dataType = getDataType(operands.get(0));
		} else if (operator == OperatorType.OP_ARRAY_CONCAT) {
			// 数组CONCAT
			assert operands.size() > 0;
			String elementType = getDataType(operands.get(0));
			String[] typeInfoList = elementType.split("_");
			if (typeInfoList.length == 1) {
				// 0维数组
				this.dataType = typeInfoList[0] + "_1";
			} else {
				int dimCount = Integer.parseInt(typeInfoList[1]);
				this.dataType = typeInfoList[0] + "_" + String.valueOf(dimCount + 1);
			}
		} else if (operator == OperatorType.OP_ADD || operator == OperatorType.OP_MINU || operator == OperatorType.OP_MUL
				|| operator == OperatorType.OP_DIV || operator == OperatorType.OP_MOD) {
			// 加减乘除模，取最高级类型
			// bool->int->long->longlong->double
			Integer typePriority = 0;
			for (AstItem x : operands) {
//				System.out.println(getDataType(x));
				String[] typeInfoList = getDataType(x).split("_");
				// 不允许数组加减乘除
				assert typeInfoList.length == 1 && typeInfoList[0] != "string";
				if (type2priority.get(typeInfoList[0]) > typePriority) {
					typePriority = type2priority.get(typeInfoList[0]);
				}
			}
			this.dataType = priority2type.get(typePriority);
			if (getDataType(operands.get(0)).equals("char") && getDataType(operands.get(1)).equals("char") 
					&& operator == OperatorType.OP_MINU) {
				this.dataType = "int";
			}
			if (getDataType(operands.get(0)).equals("char") && getDataType(operands.get(1)).equals("int") 
					&& (operator == OperatorType.OP_MINU || operator == OperatorType.OP_ADD)) {
				this.dataType = "char";
			}
		} else {
			// bool运算
			this.dataType = "bool";
		}
		assert this.dataType != null;
	}

	@Override
	public String toJsonString() {
		String json = "{";
		json += "\"Type\": \"Expression\",";
		json += "\"Operator\": \"" + operator.toString() + "\",";
		json += "\"Operands\": [";
		for (AstItem astItem: operands) {
			json += astItem.toJsonString() + ",";
		}
//		json += "],";
//		json += "AllUnaryExp: [";
//		for (AstItem astItem: allUnaryExp) {
//			json += astItem.toJsonString() + ",";
//		}
		json += "]}";
		return json;
	}
}
